package cn.hinglo.common.generator.ts;

import cn.hinglo.common.generator.DatabaseToBeanGenerator;
import cn.hinglo.common.interceptors.base.BaseRequestMethodInterceptor;
import cn.hinglo.common.util.RoutesKit;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import com.jfinal.aop.Before;
import com.jfinal.aop.Interceptor;
import com.jfinal.config.Routes;
import com.jfinal.core.ActionKey;
import com.jfinal.core.Controller;
import com.jfinal.core.NotAction;
import com.jfinal.kit.StrKit;
import com.jfinal.plugin.activerecord.Model;
import com.jfinal.plugin.activerecord.generator.ColumnMeta;
import com.jfinal.plugin.activerecord.generator.MetaBuilder;
import com.jfinal.plugin.activerecord.generator.TableMeta;
import com.jfinal.template.Engine;
import com.jfinal.template.Template;
import com.jfinal.upload.UploadFile;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static cn.hutool.core.util.StrUtil.SLASH;

/***
 * 全文URL权限处理
 * @author jsb-009
 */
public class PermissionUrlGenerator {
    public static String[] JAVA_TYPE = {
            "java.lang.Float",
            "java.lang.Double",
            "java.lang.Integer",
            "java.lang.Long",
            "java.lang.Short",
            "java.lang.String",
            "java.lang.Boolean",
            "java.util.Date"
    };

    public static Map<String, String> JAVA_TO_TS = new HashMap<>();

    static {
        JAVA_TO_TS.put("java.lang.Float", "number");
        JAVA_TO_TS.put("java.lang.Double", "number");
        JAVA_TO_TS.put("java.lang.Integer", "number");
        JAVA_TO_TS.put("java.lang.Long", "number");
        JAVA_TO_TS.put("java.lang.Short", "number");
        JAVA_TO_TS.put("java.lang.String", "string");
        JAVA_TO_TS.put("java.lang.Boolean", "boolean");
        JAVA_TO_TS.put("java.util.Date", "Date");

        JAVA_TO_TS.put("boolean", "boolean");
        JAVA_TO_TS.put("char", "string");
        JAVA_TO_TS.put("short", "number");
        JAVA_TO_TS.put("int", "number");
        JAVA_TO_TS.put("long", "number");
        JAVA_TO_TS.put("float", "number");
        JAVA_TO_TS.put("double", "number");
    }


    public static void main(String[] args) {
        /***
         * 所有的api接口集合
         */
        List<MethodEntity> result = new ArrayList<>();
        /***
         * 针对对象参数处理
         */
        List<ObjectEntity> param = new ArrayList<>();


        Routes r = new Routes() {
            @Override
            public void config() {

            }
        };
        RoutesKit.initRoutes(r);
        List<Routes> routesList = Routes.getRoutesList();
        Class<?> dc;
        for (Routes routes : routesList) {
            for (Routes.Route route : routes.getRouteItemList()) {
                Class<? extends Controller> controllerClass = route.getControllerClass();
                boolean declaredMethods = !routes.getMappingSuperClass() || controllerClass.getSuperclass() == Controller.class;
                Method[] methods = (declaredMethods ? controllerClass.getDeclaredMethods() : controllerClass.getMethods());
                List<ApiEntity> resultList = new ArrayList<>();
                for (Method method : methods) {
                    if (declaredMethods) {
                        if (!Modifier.isPublic(method.getModifiers())) {
                            continue;
                        }
                    } else {
                        dc = method.getDeclaringClass();
                        if (dc == Controller.class || dc == Object.class) {
                            continue;
                        }
                    }
                    if (method.getAnnotation(NotAction.class) != null) {
                        continue;
                    }
                    String controllerKey = route.getControllerKey();

                    String methodName = method.getName();
                    ActionKey ak = method.getAnnotation(ActionKey.class);
                    String actionKey;
                    if (ak != null) {
                        actionKey = ak.value().trim();
                        if ("".equals(actionKey)) {
                            throw new IllegalArgumentException(controllerClass.getName() + "." + methodName + "(): The argument of ActionKey can not be blank.");
                        }

                        if (!actionKey.startsWith(SLASH)) {
                            actionKey = SLASH + actionKey;
                        }
                    } else if ("index".equals(methodName)) {
                        actionKey = controllerKey;
                    } else {
                        actionKey = controllerKey.equals(SLASH) ? SLASH + methodName : controllerKey + SLASH + methodName;
                    }
                    ApiEntity apiEntity = new ApiEntity();
                    apiEntity.setUrl(actionKey).setMethodName(methodName).setMethod(getMethodName(method));
                    paramKit(method, apiEntity, param);
                    resultList.add(apiEntity);
                }
                if (resultList.size() > 0) {
                    MethodEntity methodEntity = new MethodEntity();
                    methodEntity.setClassName(StrUtil.removeSuffix(controllerClass.getSimpleName(), "Controller"));
                    methodEntity.setMethodColumn(resultList);
                    result.add(methodEntity);
                }
            }
        }

        // Java 模型处理
        modelKit(param);
        // 文件写入
        fileWrite(param, result);
    }


    /***
     * 网络请求方式处理
     * @param method 对象
     * @return 返回结构（get,post,put,delete）
     */
    public static String getMethodName(Method method) {
        Before annotation = method.getAnnotation(Before.class);
        if (annotation != null) {
            Class<? extends Interceptor>[] value = annotation.value();
            for (Class<? extends Interceptor> aClass : value) {
                if (BaseRequestMethodInterceptor.class.isAssignableFrom(aClass)) {
                    return aClass.getSimpleName().toLowerCase();
                }
            }
        }
        return "get";
    }


    /***
     * 参数处理
     * @param method 方法
     * @param apiEntity api 信息
     * @return
     */
    public static void paramKit(Method method, ApiEntity apiEntity, List<ObjectEntity> param) {
        Parameter[] parameters = method.getParameters();
        List<ColumnEntity> columnList = new ArrayList<>();
        for (Parameter parameter : parameters) {
            ColumnEntity column = new ColumnEntity();
            column.setColumnName(parameter.getName()).setJavaType(parameter.getType().getName());
            // 如果是基本数据类型 或者是包装类型
            if (parameter.getType().isPrimitive() || ArrayUtil.contains(JAVA_TYPE, parameter.getType().getName())) {
                column.setTsType(javaToTsType(parameter.getType().getName()));
                // 如果是集合类型与数组类型
            } else if (List.class.isAssignableFrom(parameter.getType()) || parameter.getType().isArray()) {
                column.setTsType("Array");
                // 如果是Jfinal 的model
            } else if (Model.class.isAssignableFrom(parameter.getType())) {
                // 如果是实体Bean 就使用实体bean作为数据类型
                column.setTsType(parameter.getType().getSimpleName());
                // 如果是文件上传对象
            } else if (UploadFile.class.isAssignableFrom(parameter.getType())) {
                apiEntity.setIsFile(true);
                column.setTsType("string");
            } else {
                column.setTsType(parameter.getType().getSimpleName());
                // 就是普通的javaBean
                Class<?> type = parameter.getType();
                type.getSimpleName();
                ObjectEntity objectEntity = new ObjectEntity();
                objectEntity.setClassName(type.getSimpleName());
                Field[] fields = type.getFields();
                List<ColumnEntity> columnEntityList = new ArrayList<>();
                for (Field field : fields) {
                    ColumnEntity columnEntity = new ColumnEntity();
                    columnEntity.setColumnName(field.getName());
                    columnEntity.setJavaType(field.getType().getName());
                    columnEntity.setTsType(javaToTsType(field.getName()));
                }
                objectEntity.setColumn(columnEntityList);
                param.add(objectEntity);
            }
            columnList.add(column);
        }
        apiEntity.setColumn(columnList);
    }

    /***
     * 根据Java 类型返回Ts 类型
     * @param type Java类型的字符串
     * @return 返回结果
     */
    public static String javaToTsType(String type) {
        return JAVA_TO_TS.containsKey(type) ? JAVA_TO_TS.get(type) : "object";
    }

    public static  List<TableMeta> getTableInfo(){
        MetaBuilder metaBuilder = new MetaBuilder(DatabaseToBeanGenerator.getDataSource());
       return metaBuilder.build();
    }
    /***
     * Java 数据库模型处理
     * @param param 模型参数集合
     */
    public static void modelKit(List<ObjectEntity> param) {
        List<TableMeta> TableMetas = getTableInfo();
        for (TableMeta tableMeta : TableMetas) {
            ObjectEntity objectEntity = new ObjectEntity();
            objectEntity.setClassName(buildModelName(tableMeta.name));
            objectEntity.setColumn(new ArrayList<>());
            List<ColumnMeta> columnMetas = tableMeta.columnMetas;
            for (ColumnMeta columnMeta : columnMetas) {
                ColumnEntity columnEntity = new ColumnEntity();
                columnEntity.setColumnName(columnMeta.name);
                columnEntity.setJavaType(columnMeta.javaType);
                columnEntity.setTsType(javaToTsType(columnMeta.javaType));
                objectEntity.getColumn().add(columnEntity);
            }
            param.add(objectEntity);
        }
    }

    /***
     * 处理表名称，由下划线转驼峰
     * @param tableName 表名称
     * @return
     */
    private static String buildModelName(String tableName) {
        // 需要移除的指定的前缀
        String[] prefixes = {"sys_", "core_"};
        // 移除表名前缀仅用于生成 modelName、baseModelName，而 tableMeta.name 表名自身不能受影响
        for (String prefix : prefixes) {
            if (tableName.startsWith(prefix)) {
                tableName = tableName.replaceFirst(prefix, "");
                break;
            }
        }
        return StrKit.firstCharToUpperCase(StrKit.toCamelCase(tableName));
    }
    public static void fileWrite(List<ObjectEntity> param, List<MethodEntity> result) {
        Engine engine = Engine.create("myEngine");
        engine.setDevMode(true);
        engine.setToClassPathSourceFactory();
        engine.addSharedMethod(new StrKit());
        Template template = engine.getTemplate("cn/hinglo/common/generator/ts/apiTemplate.jf");
        Map<String,Object> data=new HashMap<>(1);
        data.put("interface",param);
        data.put("clazz",result);
        String dataController = template.renderToString(data);
        try (OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("E:\\Maven\\permissionPC\\common\\api.ts"), StandardCharsets.UTF_8)) {
            osw.write(dataController);
        } catch (IOException e) {
            e.printStackTrace();
        }


    }

}
